20240806 모각코 활동 6회차

오늘의목표
CNN 실습 - MNIST 이미지 분류 ( RESNET은 7회차에 진행할 예정 )

import torch #pytorch 가져오기

데이터 가져오기

#데이터셋불러오고 텐서로 바꿔주기

from torchvision import datasets #데이터셋 불러오고

from torchvision.transforms import ToTensor #텐서로 바꿔주기

  

#datasets에서 MNIST 가져와서 훈련데이터와 테스트데이터 가져와주기.

#datasets.MNIST(root - 데이터가 저장될 경로, train - train이 true 이면 train data이고 false면 test data, download - 데이터 없으면 인터넷에서 다운로드해줌 , transform - transform을 ToTensor로 지정해주지 않으면 텐서의 형식이 아닌, PIL이미지로 데이터가 가져와지게 된다)

  

train_data = datasets.MNIST(

    root = "data",

    train = True, #train data를 다운로드

    transform = ToTensor(),

    download = True

)

test_data = datasets.MNIST(

    root = 'data',

    train = False, #test data를 다운로드

    transform = ToTensor()

)

데이터 확인하기

#학습데이터 확인

print(train_data)

print(train_data.data.size())

# 데이터셋의 이름은 MNIST

# 데이터의 수는 60000개

# 훈련데이터

# StandardTransform(데이터셋에 일관되게 적용되는 변환의 표준을 정의) -> Transform: ToTensor() #이미지 데이터들을 모두 일관되게 텐서 형태로 변환하겠다는 것을 의미.


#테스트데이터 확인

print(test_data)

print(test_data.data.size())

#데이터의 수가 10000 인 것과 테스트데이터라는 것을 제외하면 나머지 속성은 학습데이터와 동일함.
#데이터 시각적으로 확인

import matplotlib.pyplot as plt #시각적 확인을 위해 matplotlib을 사용.

fig, ax = plt.subplots() # fig -> 데이터가 담기는 프레임 / ax -> 실제 데이터가 그려지는 캔버스

ax.imshow(train_data.data[0], cmap='gray') #데이터의 모습



#이미지 위에 각 픽셀 값을 표시해서 나타내보기

for i in range(train_data.data[0].shape[0]): # i와j는 텍스트를 표시할 위치를 지정하기 위함.

  for j in range(train_data.data[0].shape[1]):

    c = 1 if train_data.data[0][i, j].item() < 125 else 0 # 이미지의 각 픽셀 값( train_data.data[0][i,j].item() )이 125보다 작으면 c = 1 흰색을 사용, 크면 c = 0 검정 사용.

    ax.text(j, i, str(train_data.data[0][i, j].item()), color=(c, c, c), ha='center', va='center', fontsize=5) # text()를 사용하여 이미지 위에 텍스트 그리기

  

plt.title("%i" % train_data.targets[0])

plt.show

mnist_1

데이터 준비하기

from torch.utils.data import DataLoader
# DataLoader ->  데이터를 미니배치 형태로 만들어서 우리가 실제로 학습할 때 이용할 수 있도록 함.
#DataLoader(dataset 데이터 , batch_size=1 한 번의 배치 안에 있는 샘플 사이즈, shuffle=False 데이터셋을 섞어서 데이터가 학습되는 순서를 바꿈, num_workers=0 동시에 처리하는 프로세서의 수. 하나 더 추가하면 20%정도 속도가 빨라짐.)
#배치 학습 -> 전체 데이터를 n등분 하여 학습.

loaders = {

    'train' : torch.utils.data.DataLoader(train_data,

                                          batch_size=100,

                                          shuffle=True,

                                          num_workers=1),

    'test' : torch.utils.data.DataLoader(test_data,

                                         batch_size=100,

                                         shuffle=True,

                                         num_workers=1)

}

loaders

CNN 모델 설정하기

class CNN(torch.nn.Module):

  

  def __init__(self):

    super(CNN, self).__init__()

    self.layer1 = torch.nn.Sequential(

        torch.nn.Conv2d(1, 16, kernel_size=5, stride=1, padding=2), #컨볼루션 레이어(합성곱층) #1차원(1개채널) 데이터를 받아 16개의 feature(16개의채널)로 나누겟다!!임.

        torch.nn.ReLU(), #ReLU층

        torch.nn.MaxPool2d(kernel_size=2, stride=2)) #풀링층

    self.layer2 = torch.nn.Sequential(

        torch.nn.Conv2d(16, 32, kernel_size=5, stride=1, padding=2),

        torch.nn.ReLU(),

        torch.nn.MaxPool2d(kernel_size=2, stride=2))
        
       
        # layer 1, layer2 층까지는 이미지를 형상으로 분할하고 분석하는 부분
        # 다음 fc 층에서는 이미지를 분류 예측하는 부분.
        

    self.fc = torch.nn.Linear(32 * 7 * 7, 10, bias=True) #32*7*7만큼의 입력을 linear레이어에 의해 계산되게 해서... 10개의 출력( MNIST 이미지를 0부터 9까지 분류해야하기때문 )이 나오도록 함.

    torch.nn.init.xavier_uniform_(self.fc.weight) # 신경망의 가중치를 초기화 ( 신경망의 가중치를 학습 전에 적절한 값으로 설정하는 과정 )



    # __init__에서는 필요한 레이어들을 정의내렸다고 볼 수 있음.
    # 아래 forward(얘가 실제적인 모델의 형태가 됨)에서 사용한다.

  def forward(self, x): #순전파 #순전파만 지정해주어도 pytorch에서는 역전파 과정을 매우 쉽게 할 수 있도록 해준다.

    out = self.layer1(x)

    out = self.layer2(out)

    out = out.view(out.size(0), -1) #  view() 함수는 텐서의 크기를 변경하는 데 사용 # 데이터를 완전 연결(fc) 층에 전달하기 위해 2차원 또는 3차원 텐서를 1차원 벡터로 평탄화 하는 과정이 필요함.
    out = self.fc(out)

    return out
model = CNN()

model

CNN(
(layer1): Sequential(
(0): Conv2d(1, 16, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
(1): ReLU()
(2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)
(layer2): Sequential(
(0): Conv2d(16, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
(1): ReLU()
(2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)
(fc): Linear(in_features=1568, out_features=10, bias=True)
)


학습하기

learning_rate = 0.01 # 파라미터를 얼마나 업데이트할 것인지를 결정. 학습률, step size. 너무 크지도 작지도 않아야 함.

loss_func = torch.nn.CrossEntropyLoss() # 모델 예측과 실제값 간의 차이를 측정하는 손실함수.

optimizer =  torch.optim.Adam(model.parameters(), lr=learning_rate) # 손실함수를 통해 나온 을 최소화하기 위해 가중치를 업데이트하는 방법

training_epochs = 10 # 전체 데이터셋을 몇 번 반복할 것인지 결정.
# 반복의 횟수는 epoch과 batch의 크기에 따라 결정

total_batch = len(loaders['train'])

for epoch in range(training_epochs):

  avg_cost = 0

  for X, Y in loaders['train']:

    optimizer.zero_grad() # 학습에서, 역전파를 거칠 때 마다 각 .grad 값에 변화도가 저장이 되는데,  이어지는 다음 학습에서 .grad의 값을 0으로 초기화시켜주지 않으면 이전에 저장된 변화도 값이 다음 학습에 영향을 주기 때문에 원하는 방향으로 학습하기 힘들다. 그래서zero_grad를 통해 .grad 의 값들을 0으로 초기화시켜준다.

    pred = model(X) #순전파

    cost = loss_func(pred, Y) #손실함수계산

    cost.backward() #역전파

    optimizer.step() # 역전파 단계에서 수집된 변화도로 매개변수를 조정

  

    avg_cost += cost / total_batch

  

  print('[Epoch: {:>4}] cost = {:>.9}'.format(epoch + 1, avg_cost))
  # `epoch + 1` 값을 최소 4칸의 너비로 오른쪽 정렬하여 출력
  # `avg_cost` 값을 최소 9자리까지 나타내어 오른쪽 정렬하여 출력

print('Learning Finished....>_<')

[Epoch: 1] cost = 0.0461711548
[Epoch: 2] cost = 0.0472225286
[Epoch: 3] cost = 0.0413064063
[Epoch: 4] cost = 0.0417594947
[Epoch: 5] cost = 0.0395734794
[Epoch: 6] cost = 0.0441303253
[Epoch: 7] cost = 0.0408433564
[Epoch: 8] cost = 0.043582622
[Epoch: 9] cost = 0.0441764817
[Epoch: 10] cost = 0.0412645154
Learning Finished....>_<